import os

from pyparsing import *

from venya.lib import exception
from venya.lib import ip
from venya.lib import stdlib
from venya.lib import trie

import bgperr
import routemap

class BgpCompiler(object):
   def __init__(self):
      self._pfl = trie.Tree()
      self._rtm = trie.Tree()

   def addPrefixList(self, PrefixList):
      self._pfl.insert(PrefixList)

   def findPrefixList(self, Name):
      return self._pfl.find(Name)

   def CheckPrefixList(self, Name):
      test = self.findPrefixList(Name)
      if test:
         retval = True
      else:
         retval = False

      return retval

   def addRouteMap(self, RouteMap):
      self._rtm.insert(RouteMap)

   def findRouteMap(self, Name):
      return self._rtm.find(Name)

class BgpConfig(object):
   def __init__(self, Filename):
      super(BgpConfig, self).__init__()
      self._compiler = BgpCompiler()
      if Filename:
         self._filename = Filename
      else:
         bgperr.Error("Invalid or no filename!")

      self._fh = None
      self._ip = Word(nums + ".").setParseAction(self._checkip)
      self._ipnet = Word(nums + "." + "/").setParseAction(self._checkip)
      self._alphas = Word(alphanums + "_" + "-")
      self._lp, self._rp = map(Suppress, "{}")
      self._lb, self._rb = map(Suppress, "()")
      self._quoted = quotedString.copy().setParseAction(removeQuotes)
      self._desc = "description" + self._quoted
      self._pfl = "prefix-list" + self._alphas

   def _checkip(self, string, location, tokens):
      retval = None
      try:
         retval = ip.ip(str(tokens[0]))
      except exception.IPError as err:
         bgperr.ConfigError(err)

      return retval

   def _addPrefixList(self, string, location, tokens):
      pfl = tokens[0]
      prefixlist = routemap.PrefixList(pfl[1])
      if str(pfl[2]) == "description":
         prefixlist.AddDescr(pfl[3])
         index = 4
      else:
         index = 2

      prefixlist.AddNetworks(*pfl[index:])
      self._compiler.addPrefixList(prefixlist)
      return prefixlist

   def _checkpref(self, string, location, tokens):
      retval = 0
      preference = int(tokens[1])
      if preference < 0:
         raise bgperr.ConfigError("Invalid local preference defined on line %d",
               lineno(location, string))

      return tokens

   def _checkpfl(self, string, location, tokens):
      if not self._compiler.CheckPrefixList(str(tokens[1])):
         raise bgperr.ConfigError("undefined prefix list on line: %d" %lineno(
            location, string))

   def _addRouteMap(self, string, location, tokens):
      rtemap = None
      print(tokens)
      if len(tokens) == 1:
         rtemap = routemap.RouteMap(str(tokens[0]))
         self._compiler.addRouteMap(rtemap)

      return rtemap

   def _addRouteMapAttrs(self, string, location, tokens):
      tk = tokens[0]
      rtemap = self._compiler.findRouteMap(str(tk[0]))
      count = 1
      if rtemap:
         for ix in tk[1:]:
            if str(ix) == "description":
               rtemap.AddDescr(tk[count + 1])
            elif str(ix) == "prefix-list":
               rtemap.AddPrefixList(self._compiler.findPrefixList(
                  str(tk[count + 1])))
            elif str(ix) == "preference":
               rtemap.AddLocalPref(int(tk[count + 1]))
            
            count += 1
      else:
         raise bgperr.ConfigError("undefined route map defined!")

      return rtemap

   def parsePrefixList(self):
      return Optional(ZeroOrMore(Group(
         self._pfl + self._lp + Optional(self._desc) + OneOrMore(
            self._ipnet | self._ip) +
         self._rp).setParseAction(self._addPrefixList)))

   def parseRouteMap(self):
      print("here!")
      prefstmt = ("preference" + Word(nums)).setParseAction(self._checkpref)
      print("here!")
      rtestmt = (Suppress(
         "route-map") + self._alphas + Optional(Word(nums))).setParseAction(
            self._addRouteMap)

      print("here!") 
      return Optional(ZeroOrMore(Group(
         rtestmt + self._lp + Optional(self._desc) + Optional(
         self._pfl).setParseAction(self._checkpfl) + Optional(
            prefstmt) + self._rp).setParseAction(self._addRouteMapAttrs)))

   def parse(self):
      retval = None
      """
      pflstmt = "prefix-list" + self._alphas
      descstmt = Literal("description") + self._quoted
      rtestmt = (Suppress("route-map") + self._alphas + Optional(
         Word(nums))).setParseAction(self._addRouteMap)
      asnstmt = "as-number" + Word(nums)
      pgstmt = "peer-group" + self._alphas

      parsepfl = Group(pflstmt + self._lp + Optional(descstmt) + OneOrMore(
         self._ipnet | self._ip) + self._rp).setParseAction(self._addPrefixList)
      parserte = Group(rtestmt + self._lp + Optional(descstmt) + Optional(
         pflstmt).setParseAction(self._checkpfl) + Optional(
            prefstmt) + self._rp).setParseAction(self._addRouteMapAttrs)
      parsepg = Group(pgstmt + self._lp + Optional(descstmt) +

      defpfl = Optional(ZeroOrMore(parsepfl))
      defrte = Optional(ZeroOrMore(parserte))

      """

      ParserElement.setDefaultWhitespaceChars('\t ')
      rule = self.parsePrefixList() + self.parseRouteMap()
      rule.ignore(Literal('#') + LineEnd())

      results = None
      try:
         with open(self._filename, "r") as fh:
            results = rule.parseString(fh.read())
            retval = self._compiler
      except ParseFatalException as err:
         raise bgperr.ConfigError("parsing failed on %s: %s", self._file, err)
      except ParseException as err:
         raise bgperr.ConfigError("syntax error on line %d in file %s: %s",
               err.lineno, self._file, err.msg)
      except bgperr.ConfigError as err:
         raise bgperr.ConfigError(str(err))

      return results
